import {Plane,Vector3} from 'three';
import Constant from './ui-constant';
import UpdateManager from './ui-update-manager';

export default function UIMesh(Base=class {}){
    return class UIMesh extends Base{
        constructor(options={}){
            super(options);
            this.states={};
            this.currentState=undefined;
            this.isUI=true;
            this.autoLayout=true;
        }

        get uiType(){
            return Constant.TYPE_COMPONENT_MESH;
        }

        getLineSpace(){
            return this.lineSpace||Constant.DEFAULT[Constant.STYLE_LINE_SPACE];
        }
        getContentDirection() {
            return this.contentDirection || Constant.DEFAULT[Constant.STYLE_CONTENT_DIRECTION];
        }
        getJustifyContent() {
            return this.justifyContent || Constant.DEFAULT[Constant.STYLE_JUSTIFY_CONTENT];
        }
        getInterLine() {
            return (this.interLine === undefined) ? Constant.DEFAULT[Constant.STYLE_INTER_LINE]: this.interLine;
        }
        getOffset() {
            return (this.offset === undefined) ? Constant.DEFAULT[Constant.STYLE_OFFSET] : this.offset;
        }
        getBackgroundSize() {
            return (this.backgroundSize === undefined) ? Constant.DEFAULT[Constant.STYLE_BACKGROUND_SIZE] : this.backgroundSize;
        }
        getAlignContent() {
            return this.alignContent || Constant.DEFAULT[Constant.STYLE_ALIGN_CONTENT];
        }
        getBackgroundTexture() {
            return this.backgroundTexture || Constant.DEFAULT[Constant.STYLE_BACKGROUND_TEXTURE]();
        }
        getBackgroundColor() {
            return this.backgroundColor || Constant.DEFAULT[Constant.STYLE_BACKGROUND_COLOR];
        }
        getBackgroundOpacity() {
            return ( (!this.backgroundOpacity) && (this.backgroundOpacity !== 0 )) ?
                Constant.DEFAULT[Constant.STYLE_BACKGROUND_OPACITY] : this.backgroundOpacity;
        }
        getParentsNumber( i ) {
            let _i = i || 0;
            if ( this.getUIParent() ) {
                return this.parent.getParentsNumber( _i + 1 );
            }
            return _i;
        }
        getContainer() {
            if ( !this.threeOBJ && this.parent ) {
                return this.parent.getContainer();
            } else if ( this.threeOBJ ) {
                return this;
            }
            return Constant.DEFAULT.CONTAINER;
        }
        getFontSupersampling() {
            return this._getProperty(Constant.STYLE_FONT_SUPERSAMPLING);
        }
        getFontOpacity() {
            return this._getProperty(Constant.STYLE_FONT_OPACITY);
        }
        getFontPXRange() {
            return this._getProperty(Constant.STYLE_FONT_PX_RANGE);
        }
        getFontWeight(){
            return this._getProperty(Constant.STYLE_FONT_WEIGHT);
        }
        getBorderRadius() {
            return this._getProperty(Constant.STYLE_BORDER_RADIUS);
        }
        getBorderWidth() {
            return this._getProperty(Constant.STYLE_BORDER_WIDTH);
        }
        getBorderColor() {
            return this._getProperty(Constant.STYLE_BORDER_COLOR);
        }
        getBorderOpacity() {
            return this._getProperty(Constant.STYLE_BORDER_OPACITY);
        }
        getFontFamily() {
            return this._getProperty(Constant.STYLE_FONT_FAMILY);
        }
        getScaleRatio() {
            return (this.scaleRatio === undefined) ? Constant.DEFAULT[Constant.STYLE_SCALE_RATIO] : this.scaleRatio;
        }
        getTextBaseLine(){
            return this._getProperty(Constant.STYLE_TEXT_BASE_LINE);
        }
        getBreakOn() {
            return this._getProperty(Constant.STYLE_BREAK_ON);
        }
        getWhiteSpace() {
            return this._getProperty(Constant.STYLE_WHITE_SPACE);
        }
        getTextType() {
            return this._getProperty(Constant.STYLE_TEXT_TYPE);
        }
        getFontColor() {
            return this._getProperty(Constant.STYLE_FONT_COLOR);
        }
        getFontTexture() {
            return this._getProperty(Constant.STYLE_FONT_TEXTURE);
        }
        getLetterSpacing() {
            return this._getProperty(Constant.STYLE_LETTER_SPACING);
        }
        getFontKerning() {
            return this._getProperty(Constant.STYLE_FONT_KERNING);
        }
        getFontSize() {
            return this._getProperty(Constant.STYLE_FONT_SIZE);
        }
        getUIChildren() {
            return this.children.filter( (child)=> {
                return child.isUI;
            });
        }
        getHiddenOverflow() {
            return (this.hiddenOverflow === undefined) ? Constant.DEFAULT[Constant.STYLE_HIDDEN_OVERFLOW] : this.hiddenOverflow;
        }
        getClippingPlanes() {
            const planes = [];
            if ( this.parent && this.parent.isUI ) {
                // if ( this.isBlock && this.parent.getHiddenOverflow() ) {
                if ( this.parent.isBlock && this.parent.getHiddenOverflow() ) {
                    const yLimit = ( this.parent.getHeight() / 2 ) - ( this.parent.padding || 0 );
                    const xLimit = ( this.parent.getWidth() / 2 ) - ( this.parent.padding || 0 );
                    const newPlanes = [
                        new Plane( new Vector3( 0, 1, 0 ), yLimit ),
                        new Plane( new Vector3( 0, -1, 0 ), yLimit ),
                        new Plane( new Vector3( 1, 0, 0 ), xLimit ),
                        new Plane( new Vector3( -1, 0, 0 ), xLimit )
                    ];
                    newPlanes.forEach( plane => {
                        plane.applyMatrix4( this.parent.matrixWorld );
                    });
                    planes.push( ...newPlanes );
                }
                if ( this.parent.parent && this.parent.parent.isUI ) {
                    planes.push( ...this.parent.getClippingPlanes() );
                }
            }
            return planes;
        }
        getContentDirection() {
            return this.contentDirection || Constant.DEFAULT[Constant.STYLE_CONTENT_DIRECTION];
        }
        getBestFit() {
            return (this.bestFit === undefined) ? Constant.DEFAULT[Constant.STYLE_BEST_FIT] : this.bestFit;
        }
        getUIParent() {
            if ( this.parent && this.parent.isUI ) {
                return this.parent;
            }
            return null;
        }
        getHighestParent() {
            if ( !this.getUIParent() ) {
                return this;
            }
            return this.parent.getHighestParent();
        }

        set( options ) {
            let parsingNeedsUpdate, layoutNeedsUpdate, innerNeedsUpdate;
            UpdateManager.register( this );
            if ( !options || JSON.stringify(options) === JSON.stringify({}) ) return;
            for ( const prop of Object.keys(options) ) {
                if ( this[prop] !== options[prop] ) {
                    switch ( prop ) {
                        case Constant.STYLE_CONTENT :
                        case Constant.STYLE_FONT_SIZE :
                        case Constant.STYLE_FONT_KERNING:
                        case Constant.STYLE_BREAK_ON:
                        case Constant.STYLE_WHITE_SPACE:
                            if ( this.isText ) parsingNeedsUpdate = true;
                            layoutNeedsUpdate = true;
                            this[ prop ] = options[ prop ];
                            break;
                        case Constant.STYLE_BEST_FIT:
                            if ( this.isBlock ) {
                                parsingNeedsUpdate = true;
                                layoutNeedsUpdate = true;
                            }
                            this[ prop ] = options[ prop ];
                            break;
                        case Constant.STYLE_WIDTH:
                        case Constant.STYLE_HEIGHT:
                        case Constant.STYLE_PADDING:
                            if ( this.isInlineBlock || ( this.isBlock && this.getBestFit() !== Constant.VALUE_NONE )) parsingNeedsUpdate = true;
                            layoutNeedsUpdate = true;
                            this[ prop ] = options[ prop ];
                            break;
                        case Constant.STYLE_LETTER_SPACING:
                        case Constant.STYLE_INTER_LINE:
                            if ( this.isBlock && this.getBestFit() !== Constant.VALUE_NONE ) parsingNeedsUpdate = true;
                            layoutNeedsUpdate = true;
                            this[ prop ] = options[ prop ];
                            break;
                        case Constant.STYLE_MARGIN:
                        case Constant.STYLE_CONTENT_DIRECTION:
                        case Constant.STYLE_JUSTIFY_CONTENT:
                        case Constant.STYLE_ALIGN_CONTENT:
                        case Constant.STYLE_TEXT_TYPE:
                        case Constant.STYLE_SRC:
                            layoutNeedsUpdate = true;
                            this[ prop ] = options[ prop ];
                            break;
                        case Constant.STYLE_FONT_COLOR:
                        case Constant.STYLE_FONT_OPACITY:
                        case Constant.STYLE_FONT_SUPERSAMPLING:
                        case Constant.STYLE_OFFSET:
                        case Constant.STYLE_BACKGROUND_COLOR:
                        case Constant.STYLE_BACKGROUND_OPACITY:
                        case Constant.STYLE_BACKGROUND_TEXTURE:
                        case Constant.STYLE_BACKGROUND_SIZE:
                        case Constant.STYLE_BORDER_RADIUS:
                        case Constant.STYLE_BORDER_WIDTH:
                        case Constant.STYLE_BORDER_COLOR:
                        case Constant.STYLE_BORDER_OPACITY:
                            innerNeedsUpdate = true;
                            this[ prop ] = options[ prop ];
                            break;
                        case Constant.STYLE_HIDDEN_OVERFLOW:
                            this[ prop ] = options[ prop ];
                            break;
                    }
                }
            }
            if ( options[Constant.STYLE_FONT_FAMILY] ) {
                //todo:..................................................................................change input system
                // FontLibrary.setFontFamily( this, options[Constant.STYLE_FONT_FAMILY] );
                layoutNeedsUpdate = false;
            }
            if ( options[Constant.STYLE_FONT_TEXTURE] ) {
                //todo:...................................................................................change input system
                // FontLibrary.setFontTexture( this, options[Constant.STYLE_FONT_TEXTURE] );
                layoutNeedsUpdate = false;
            }
            const parent = this.getUIParent();
            if( parent && parent.getBestFit() !== Constant.VALUE_NONE) parent.update(true, true, false);
            this.update( parsingNeedsUpdate, layoutNeedsUpdate, innerNeedsUpdate );
            if ( layoutNeedsUpdate ) this.getHighestParent().update( false, true, false );
        }
        setState( state ) {
            const savedState = this.states[ state ];
            if ( !savedState ) {
                console.warn(`state "${ state }" does not exist within this component`);
                return;
            }
            if ( state === this.currentState ) return;
            this.currentState = state;
            if ( savedState.onSet ) savedState.onSet();
            if ( savedState.attributes ) this.set( savedState.attributes );
        }

        add(...tars) {
            for ( let tar of tars ) {
                if ( tar.isInline ) this.update( null, true );
            }
            return super.add( ...tars );
        }
        remove(...tars) {
            for ( let tar of tars ) {
                if ( tar.isInline ) this.update( null, true );
            }
            return super.remove( ...tar );
        }
        clear() {
            this.traverse( (obj)=> {
                UpdateManager.disposeOf( obj );
                if ( obj.material ) obj.material.dispose();
                if ( obj.geometry ) obj.geometry.dispose();
            });
        }
        update( updateParsing, updateLayout, updateInner ) {
            UpdateManager.requestUpdate( this, updateParsing, updateLayout, updateInner );
        }
        onAfterUpdate() {}
        setupState( options ) {
            this.states[ options.state ] = {
                attributes: options.attributes,
                onSet: options.onSet
            };
        }

        _updateFontFamily( font ) {
            this.fontFamily = font;
            this.traverse( (child)=> {
                if ( child.isUI ) child.update( true, true, false );
            });
            this.getHighestParent().update( false, true, false );
        }
        _updateFontTexture( texture ) {
            this.fontTexture = texture;
            this.getHighestParent().update( false, true, false );
        }
        _getProperty( propName ) {
            if ( this[ propName ] === undefined && this.getUIParent() ) {
                return this.parent._getProperty( propName );
            } else if ( this[ propName ] !== undefined ) {
                return this[ propName ];
            }
            return Constant.DEFAULT[ propName ];
        }
    }
}